﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UtilZ.Dotnet.Ex.Base;
using UtilZ.Dotnet.Ex.Model;

namespace UtilZ.Dotnet.Ex.Communication.Net.Telnet
{
    internal class TelnetInstructions
    {

        private static byte[] _none = null;
        public static byte[] GetNone()
        {
            if (_none == null)
            {
                _none = new byte[] { (byte)ZKeys.None };
            }

            return _none;
        }




        private static readonly Dictionary<string, byte[]> _instructionDic = new Dictionary<string, byte[]>();

        private static string GetKey(ZKeys key, OSPlatformID platformID)
        {
            return $"{key.ToString()}_{platformID.ToString()}";
        }

        private static void PrimitiveSetInstruction(ZKeys key, OSPlatformID platformID, byte[] data, int length)
        {
            string keyStr = GetKey(key, platformID);
            byte[] instructions;
            if (_instructionDic.TryGetValue(keyStr, out instructions))
            {
                return;
            }


            lock (_instructionDic)
            {
                if (_instructionDic.TryGetValue(keyStr, out instructions))
                {
                    return;
                }

                instructions = data.Take(length).ToArray();
                _instructionDic.Add(keyStr, instructions);
            }
        }

        private static byte[] PrimitiveGetInstruction(ZKeys key, OSPlatformID platformID, Func<OSPlatformID, byte[]> getInstruction)
        {
            string keyStr = GetKey(key, platformID);
            byte[] instructions;
            if (_instructionDic.TryGetValue(keyStr, out instructions))
            {
                return instructions;
            }

            lock (_instructionDic)
            {
                if (_instructionDic.TryGetValue(keyStr, out instructions))
                {
                    return instructions;
                }

                instructions = getInstruction(platformID);
                _instructionDic.Add(keyStr, instructions);
                return instructions;
            }
        }






        public static void SetBackspace(OSPlatformID platformID, byte[] data, int length)
        {
            PrimitiveSetInstruction(ZKeys.Back, platformID, data, length);
        }

        public static byte[] GetBackspace(OSPlatformID platformID)
        {
            return PrimitiveGetInstruction(ZKeys.Back, platformID, (p) =>
            {
                byte[] instructions;
                switch (p)
                {
                    case OSPlatformID.Win:
                        instructions = new byte[] { 8 };
                        break;
                    case OSPlatformID.Linux_Unix:
                    case OSPlatformID.MacOS:
                        instructions = new byte[] { 127 };
                        break;
                    default:
                        throw new NotImplementedException(p.ToString());
                }
                return instructions;
            });
        }



        public static void SetDelete(OSPlatformID platformID, byte[] data, int length)
        {
            PrimitiveSetInstruction(ZKeys.Delete, platformID, data, length);
        }

        public static byte[] GetDelete(OSPlatformID platformID)
        {
            return PrimitiveGetInstruction(ZKeys.Delete, platformID, (p) =>
            {
                byte[] instructions;
                switch (p)
                {
                    case OSPlatformID.Win:
                        instructions = new byte[] { 127 };
                        break;
                    case OSPlatformID.Linux_Unix:
                    case OSPlatformID.MacOS:
                        instructions = new byte[] { 27, 91, 51, 126 };
                        break;
                    default:
                        throw new NotImplementedException(p.ToString());
                }
                return instructions;
            });
        }



        public static void SetLeftArrow(OSPlatformID platformID, byte[] data, int length)
        {
            PrimitiveSetInstruction(ZKeys.Left, platformID, data, length);
        }

        public static byte[] GetLeftArrow(OSPlatformID platformID)
        {
            //new byte[] { 27, 91, 68 }
            return PrimitiveGetInstruction(ZKeys.Left, platformID, (p) =>
            {
                byte[] instructions;
                switch (p)
                {
                    case OSPlatformID.Win:
                    case OSPlatformID.Linux_Unix:
                    case OSPlatformID.MacOS:
                        instructions = new byte[] { 27, 91, 68 };
                        break;
                    default:
                        throw new NotImplementedException(p.ToString());
                }
                return instructions;
            });
        }


        public static void SetRightArrow(OSPlatformID platformID, byte[] data, int length)
        {
            PrimitiveSetInstruction(ZKeys.Right, platformID, data, length);
        }

        public static byte[] GetRightArrow(OSPlatformID platformID)
        {
            // new byte[] { 27, 91, 67 }
            return PrimitiveGetInstruction(ZKeys.Right, platformID, (p) =>
            {
                byte[] instructions;
                switch (p)
                {
                    case OSPlatformID.Win:
                    case OSPlatformID.Linux_Unix:
                    case OSPlatformID.MacOS:
                        instructions = new byte[] { 27, 91, 67 };
                        break;
                    default:
                        throw new NotImplementedException(p.ToString());
                }
                return instructions;
            });
        }



        public static void SetEnter(OSPlatformID platformID, byte[] data, int length)
        {
            PrimitiveSetInstruction(ZKeys.Enter, platformID, data, length);
        }

        public static byte[] GetEnter(OSPlatformID platformID)
        {
            return PrimitiveGetInstruction(ZKeys.Enter, platformID, (p) =>
            {
                byte[] instructions;
                switch (p)
                {
                    case OSPlatformID.Win:
                        instructions = new byte[] { 13, 10 };
                        break;
                    case OSPlatformID.Linux_Unix:
                    case OSPlatformID.MacOS:
                        instructions = new byte[] { 13, 0 };
                        break;
                    default:
                        throw new NotImplementedException(p.ToString());
                }
                return instructions;
            });
        }


        public static byte[] GetDeleteCharInstruction(OSPlatformID platformID)
        {
            return PrimitiveGetInstruction(ZKeys.F24, platformID, (p) =>
            {
                //所有平台删除光标左侧字符指令都相同
                return new byte[] { 8, 32, 8 };
            });
        }
    }
}
